package com.timber.util;

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.table.*;
import javax.swing.plaf.metal.*;
import pv.jfcx.*;


/**
 *
 *	enhanced JPVTableView class
 *
 *	Added functionality includes:
 *	Column Configuration editor. User set columns saved in the database for specific table model.
 *	Copy to clipboard
 *	Export to csv file
 *	Find
 * Paste from clipboard: displays Column Selection Dialog and
 * calls SLTableModel.pasteFromClipBoard().
 * Picked columns MUST be in the order matching source.
 * When columns not picked, default order is used.
 *		
 *
 *	Activated through pop-up menu
 */
public class SLTableView extends JPVTableView implements SLTableMenuConstants
{
  protected static Font	defaultHeaderFont    = MetalLookAndFeel.getControlTextFont();

  protected String	widestHeaderString = "";
  protected JPopupMenu	popup = new JPopupMenu();
  protected JPopupMenu	basicPopup = new JPopupMenu();
  protected JMenuItem	colEditPB = new JMenuItem( "Edit Column Configuration..." );
  protected JMenuItem	copyPB = new JMenuItem( "Copy" );
  protected JMenuItem	pastePB = new JMenuItem( "Paste" );
  protected JMenuItem	exportCsvPB = new JMenuItem( "Export..." );
  protected JMenuItem	excelPB = new JMenuItem( "Excel..." );
  protected JMenuItem	importCsvPB = new JMenuItem( "Import from *.csv" );
  protected JMenuItem	findPB = new JMenuItem( "Find" );

  protected JMenuItem	copyPB_B = new JMenuItem( "Copy" );
  protected JMenuItem	exportCsvPB_B = new JMenuItem( "Export..." );
  protected JMenuItem	excelPB_B = new JMenuItem( "Excel..." );
  protected JMenuItem	findPB_B = new JMenuItem( "Find" );

  protected boolean	popupEnabled = true;

  /** display basic popup menu only	*/
  protected boolean	basicOnly = false;

  /**
   *	table view with  DEFAULT_MENU_ITEMS
   */
 public SLTableView()
  {
    this( new SLTable(), DEFAULT_MENU_ITEMS );
  }

  /**
   *	@param menuItems menu mask
   */
  public SLTableView( int menuItems )
  {
    this( new SLTable(), menuItems );
  }

  public SLTableView( SLTableModel model )
  {
    this( new SLTable( model ), DEFAULT_MENU_ITEMS );
  }

  public SLTableView( SLTableModel model, int menuItems )
  {
    this( new SLTable( model ), menuItems );
  }

  public SLTableView( SLTable table )
  {
    this( table, DEFAULT_MENU_ITEMS );
  }


  /**
   **	Create table view with specified popup menu items
   **
   **	@params	table
   **	@params	menuItems	menu mask
   */
  public SLTableView( SLTable table, int menuItems )
  {
    super( table );
    setHeaderMultiLine( 4 );
    setHeaderAlignment( SwingConstants.CENTER );
    setHeaderFont( defaultHeaderFont );
    setReorderingAllowed( false );
    setScrollAfterSort( false );

    //	column config
    if( ( menuItems & CONFIG_COLUMN_MENU ) > 0 )
    {
      colEditPB.addActionListener( new ActionListener()
	{
	  public void actionPerformed( ActionEvent e )
	  {
	    colEditPB_actionPerformed();
	  }
	} );
      popup.add( colEditPB );
      popup.addSeparator();
    }

    //	copy to clipboard
    if( ( menuItems & COPY_MENU ) > 0 )
    {
      copyPB.addActionListener( new ActionListener()
	{
	  public void actionPerformed( ActionEvent e )
	  {
	    copyPB_actionPerformed();
	  }
	} );
      popup.add( copyPB );

      copyPB_B.addActionListener( new ActionListener()
	{
	  public void actionPerformed( ActionEvent e )
	  {
	    copyPB_actionPerformed();
	  }
	} );
      basicPopup.add( copyPB_B );
    }

    //	paste from clipboard
    if( ( menuItems & PASTE_MENU ) > 0 )
    {
      pastePB.addActionListener( new ActionListener()
	{
	  public void actionPerformed( ActionEvent e )
	  {
	    pastePB_actionPerformed();
	  }
	} );
      popup.add( pastePB );
    }

    if( ( menuItems & EXPORT_CSV_MENU ) > 0 )
    {
      exportCsvPB.addActionListener( new ActionListener()
	{
	  public void actionPerformed( ActionEvent e )
	  {
	    export( Global.getAppComponentValue( "EXPORT_DIR" ) );
	  }
	} );
      popup.add( exportCsvPB );
      exportCsvPB_B.addActionListener( new ActionListener()
	{
	  public void actionPerformed( ActionEvent e )
	  {
	    export( Global.getAppComponentValue( "EXPORT_DIR" ) );
	  }
	} );
      basicPopup.add( exportCsvPB_B );
    }

    //	excel menu item
    if( ( menuItems & EXCEL_MENU ) > 0 )
    {
      excelPB.addActionListener( new ActionListener()
	{
	  public void actionPerformed( ActionEvent e )
	  {
	    try
	    {
	      ( ( SLTableModel )getModel() ).doExcel();
	    }
	    catch( Exception ex )
	    {
	      SLMessage.Error( null, ex, "excel", "Failed to start excel " );
	    }	  
	  }
	} );
      popup.add( excelPB );

      excelPB_B.addActionListener( new ActionListener()
	{
	  public void actionPerformed( ActionEvent e )
	  {
	    try
	    {
	      ( ( SLTableModel )getModel() ).doExcel();
	    }
	    catch( Exception ex )
	    {
	      SLMessage.Error( null, ex, "excel", "Failed to start excel " );
	    }	  
	  }
	} );      
      basicPopup.add( excelPB_B );
    }

    if( ( menuItems & IMPORT_CSV_MENU ) > 0 )
    {
      importCsvPB.addActionListener( new ActionListener()
	{
	  public void actionPerformed( ActionEvent e )
	  {
	    importFromCSV();
	  }
	} );
      popup.add( importCsvPB );
    }

    if( ( menuItems & FIND_MENU ) > 0 )
    {
      findPB.addActionListener( new ActionListener()
	{
	  public void actionPerformed( ActionEvent e )
	  {
	    findWithDialog();
	  }
	} );
      popup.add( findPB );

      findPB_B.addActionListener( new ActionListener()
	{
	  public void actionPerformed( ActionEvent e )
	  {
	    findWithDialog();
	  }
	} );
      basicPopup.add( findPB_B );

    }

    popup.addSeparator();

    addMouseListener( new MouseAdapter()
      {
	public void mouseClicked( MouseEvent me )
	{
	  doPopup( me );
	}
      } );

    addPopupToTable();
  }



  public void setHeaderFont( Font font )
  {
    super.setHeaderFont( font );
    initColStuff();
  }

  protected void addPopupToTable()
  {
    SLTable table = ( SLTable )getTable();
    table.addMouseListener( new MouseAdapter()
      {
	public void mouseClicked( MouseEvent me )
	{
	  doPopup( me );
	}
      } );
  }

  protected void initColStuff()
  {
    FontMetrics hdrFm = getFontMetrics( getHeaderFont() );
    SLTableModel tm = ( SLTableModel )getModel();
    Vector cols = tm.getColumnList();
    int maxHdrLines = 1;

    for( int i = 0; i < cols.size(); ++i )
    {
      SLColumnDefinition cd = ( ( SLColumnDefinition )cols.elementAt( i ) );
      if( cd.isVisible() )
      {
	TableColumn tc = getColumn( i );

	String dispNam = cd.getDisplayLabel();

	int hdrLines = getHeaderLinesNWidestString( dispNam );
	maxHdrLines = ( hdrLines > maxHdrLines ) ? hdrLines : maxHdrLines;

	int hdrWidth = hdrFm.stringWidth( widestHeaderString ) + 20;
	int colWidth = tc.getPreferredWidth();
	colWidth = ( colWidth > hdrWidth ) ? colWidth : hdrWidth;
	tc.setPreferredWidth( colWidth );
	tc.setWidth( colWidth );
      }
    }

    setHeaderHeight( maxHdrLines * ( hdrFm.getHeight() + 4 ) );
  }

  public int getVisibleRowCount()
  {
    return ( ( SLTable )getTable() ).getVisibleRowCount();
  }

  public void setVisibleRowCount( int cnt )
  {
    SLTable tbl = ( SLTable )getTable();
    tbl.setVisibleRowCount( cnt );
    Dimension d = getPreferredSize();
    int headerHeight = getHeaderHeight();
    int scrollBarHeight = 0;
    int scrollBarPreferredHeight = 0;
    JScrollBar hb = getHorizontalScrollBar();
    if( hb.isVisible() )
    {
      scrollBarHeight = hb.getHeight();
      Dimension hbps =  hb.getPreferredSize();
      scrollBarPreferredHeight = hbps.height;
      scrollBarHeight =
	( scrollBarHeight > scrollBarPreferredHeight ) ? scrollBarHeight : scrollBarPreferredHeight;
    }

    d.height =
      ( ( tbl.getRowHeight() + tbl.getRowMargin() ) * cnt ) +
      headerHeight +
      scrollBarHeight;
    setPreferredSize( d );
    updateUI();
  }

  /**
   **  The header string is broken into multiple lines delimited by newlines.
   **  This method returns the number of lines in the header string and sets
   **  the String widestHeaderString which is used in determining the width
   **  of the column.
   **
   **  @param headerStr		The header string for this column.
   **/
  protected int getHeaderLinesNWidestString( String headerStr )
  {
    widestHeaderString = "";
    StringTokenizer st = new StringTokenizer( headerStr, "\n" );
    int numTokens = st.countTokens();

    for( int i = 0; st.hasMoreTokens(); ++i )
    {
      String str = st.nextToken();
      if( str.length() > widestHeaderString.length() )
	widestHeaderString = str;
    }

    return numTokens;
  }

  private void colEditPB_actionPerformed()
  {
    JFrame parent = ( JFrame )SwingUtilities.getAncestorOfClass( JFrame.class, this );
    SLTableModel tm = ( SLTableModel )getModel();
    SLColumnEditorDialog ced = new SLColumnEditorDialog( parent, tm, getLockedColumns() );

    int numLockedCols = ced.showDialog();

    // numLockedCols is negative if cancel button used in the dialog
//    setLockedColumns( Math.abs(numLockedCols) );

    if (numLockedCols > -1)
       redoHeaders();
  }

  private void copyPB_actionPerformed()
  {
    SLTable tbl = ( SLTable )getTable();

    if( tbl.copySelected() <= 0 )
      Toolkit.getDefaultToolkit().beep();
  }

  /**
   *	paste button action
   */
  private void pastePB_actionPerformed()
  {

    JFrame parent = ( JFrame )SwingUtilities.getAncestorOfClass( JFrame.class, this );
    SLTableModel tm = ( SLTableModel )getModel();
    SLColumnSelectionDialog ced = new SLColumnSelectionDialog( parent, tm );

    int numLockedCols = ced.showDialog();

    Vector v = null;
    try
    {
      v = tm.pasteFromClipBoard();
    }
    catch( Exception ex )
    {
      SLMessage.Error( parent, "Paste Operation Failed\n" + ex.getMessage() );
    }

    if( v != null )
      SLMessage.Info( parent, v.size() + " Row(s) Pasted" );
  }

  /**
   *	popup dialog and execute findInTable()
   */
  public void findWithDialog()
  {

    JFrame parent = ( JFrame )SwingUtilities.getAncestorOfClass( JFrame.class, this );

    final Object[] opts = { "Find Next", "Cancel" };

    final Object[] msgs = { "Find", new JTextField(20) };

    Point pt = new Point(0, 0);


    while( true )
    {

      JOptionPane pane = new JOptionPane( msgs,
					  JOptionPane.PLAIN_MESSAGE,
					  JOptionPane.YES_NO_OPTION,
					  null,
					  opts, opts[0] );

      pane.addFocusListener( new FocusAdapter()
	{
	  public void focusGained( FocusEvent e )
	  {
	    ((JTextField)msgs[1]).requestFocus();
	  }

	} );

      JDialog dlg = pane.createDialog( parent, "Find" );

      dlg.show();

      Object retO = pane.getValue();

      if( retO == null )
	return;

      if( opts[1].equals( retO ) )
	return;

      String findStr = ((JTextField)msgs[1]).getText();


      if( findStr == null || findStr.length() == 0 )
	return;


      if( findInTable( findStr, pt ) == false )
      {
	Toolkit.getDefaultToolkit().beep();
	SLMessage.Info( findStr + " Not Found." );
	pt.setLocation( 0, 0 );
      }
      else
	pt.x++;
    }

  }


  /**
   *	search table for obj, starting at Point.
   *	scroll to row when obj is found
   *
   *	@param obj	object to search for
   *	@param p	Point starting point
   *
   *	@return	true/false
   */
  public boolean findInTable( Object obj, Point p )
  {

    SLTableModel tm = ( SLTableModel )getModel();

    boolean retCode;

    retCode = tm.search( obj, p, pv.util.PVConvert.STRING_CASE_OFF, true );

    if( retCode == true )
      ((JPVTable)getTable()).scrollTo( p.x, 0, true );

    return retCode;

  }


  public JPopupMenu getPopupMenu()
  {
    return popup;
  }

  public void setPopupMenu( JPopupMenu popup )
  {
    this.popup = popup;
  }

  public boolean getPopupEnabled()
  {
    return popupEnabled;
  }

  public void setPopupEnabled( boolean flag )
  {
    popupEnabled = flag;
  }

  public void setBasicMenuOnly( boolean flag )
  {
    basicOnly = flag;
  }

  public boolean getColumnConfigEnabled()
  {
    return colEditPB.isEnabled();
  }

  public void setColumnConfigEnabled( boolean flag )
  {
    colEditPB.setEnabled( flag );
  }

  /**
   *	popup menu at proper location based on mouse click
   */
  public void doPopup( MouseEvent me )
  {
    JPopupMenu pup;
    Point p;
    Dimension screenSize;
    Dimension pupSize;

    if( popupEnabled && SwingUtilities.isRightMouseButton( me ) )
    {
      if( basicOnly == true )
      {
	basicPopup.setInvoker( me.getComponent() );

	 p = me.getPoint();
	 screenSize = Toolkit.getDefaultToolkit().getScreenSize();
	 pupSize = basicPopup.getPreferredSize();

	SwingUtilities.convertPointToScreen( p, me.getComponent() );

	//  Make sure the popup comes up on the screen
	if( ( p.x + pupSize.width + 2 ) > screenSize.width )
	  p.x = screenSize.width - ( pupSize.width + 2 );

	if( ( p.y + pupSize.height + 30 ) > screenSize.height )
	  p.y = screenSize.height - ( pupSize.height + 30 );

	basicPopup.setLocation( p.x, p.y );
	basicPopup.setVisible( true );
      }
      else
      {
	popup.setInvoker( me.getComponent() );

	 p = me.getPoint();
	 screenSize = Toolkit.getDefaultToolkit().getScreenSize();
	 pupSize = popup.getPreferredSize();

	SwingUtilities.convertPointToScreen( p, me.getComponent() );

	//  Make sure the popup comes up on the screen
	if( ( p.x + pupSize.width + 2 ) > screenSize.width )
	  p.x = screenSize.width - ( pupSize.width + 2 );

	if( ( p.y + pupSize.height + 30 ) > screenSize.height )
	  p.y = screenSize.height - ( pupSize.height + 30 );

	popup.setLocation( p.x, p.y );
	popup.setVisible( true );
      }
    }
  }

  public void exportToCSV()
  {
    export( null );
  }


  /**
   *	export file in an excel CSV format
   */
  public void export( String exportDir )
  {
    final String csvF = "File in CSV Format";
    final String xlsF = "Excel Spreadsheet";
    final String txtF = "Tab Delimited";

    JFileChooser fc = new JFileChooser( exportDir );
    fc.addChoosableFileFilter(new SLFileFilter( new String( "txt" ), txtF ));
    fc.addChoosableFileFilter(new SLFileFilter( new String( "xls" ), xlsF ));
    fc.addChoosableFileFilter(new SLFileFilter( new String( "csv" ), csvF ));

     int ret = fc.showSaveDialog( this );

    if( ret == JFileChooser.APPROVE_OPTION )
    {
      File theFile = fc.getSelectedFile();
      if( theFile != null  )
      {
	String fileName = fc.getSelectedFile().getAbsolutePath();
        SLFileFilter ff = (SLFileFilter)fc.getFileFilter();

        if (ff.getShortDescription().equals(csvF))
        {
	  if( fileName.indexOf( '.' ) < 0 )
	    fileName += ".csv";
        }
        else if (ff.getShortDescription().equals(xlsF))
        {
	  if( fileName.indexOf( '.' ) < 0 )
	    fileName += ".xls";
        }
        else if (ff.getShortDescription().equals(txtF))
        {
	  if( fileName.indexOf( '.' ) < 0 )
	    fileName += ".txt";
        }

	theFile = new File( fileName );

	try
	{
	  if( theFile.exists() == false )
	  {
	    ret = JOptionPane.showConfirmDialog( this,
						 "Create File  " + fileName,
						 "Confirm",
						 JOptionPane.YES_NO_OPTION );

	    if( ret == JOptionPane.NO_OPTION )
	      return;
	    else
	      theFile.createNewFile();
	  }
	  else
	  {
	    ret = JOptionPane.showConfirmDialog( this,
						 "Replace File  " + fileName,
						 "Confirm",
						 JOptionPane.YES_NO_OPTION );
	    if( ret == JOptionPane.NO_OPTION )
	    {
	      return;
	    }
	  }
          // call appropriate export routine
          if (ff.getShortDescription().equals(xlsF))
	    ( ( SLTableModel )getModel() ).saveAsXls( fileName );
          else if (ff.getShortDescription().equals(txtF))
	    ( ( SLTableModel )getModel() ).saveAsTxt( theFile );
          else // save as comma delimited
	    ( ( SLTableModel )getModel() ).saveAsCSV( theFile );

	  SLMessage.Info( fileName + "  Saved" );
	}
	catch( Exception ex )
	{
	  SLMessage.Error( this, ex, "export", "Failed to Export File... " );
	}
      }
    }
    else
    {
      SLMessage.Info( this, "export", "No file was chosen." );
    }
  }

  /**
   **	Import file in csv format
   */
  public Vector importFromCSV()
  {
    File theFile;

    Vector v = null;

    JFileChooser fc = new JFileChooser();

    SLFileFilter ff = new SLFileFilter( new String("csv"),"File in CSV Format" );

    fc.addChoosableFileFilter(ff);

    int ret = fc.showOpenDialog(this);

    if( ret == JFileChooser.APPROVE_OPTION )
    {
      theFile = fc.getSelectedFile();
      if( theFile != null  )
      {
	String fileName = fc.getSelectedFile().getAbsolutePath();

	if( fileName.indexOf( '.' ) < 0 )
	{
	  fileName += ".csv";
	  theFile = new File( fileName );
	}
	/*

    if( 1 ==1 )
    {
      if( 1 == 1 )
      {
	String fileName = "/home/groytvar/import.csv";
	theFile = new File(fileName);
	*/

	Util.log( "Importing:  " + fileName );

	try
	{
	  if( theFile.exists() == false )
	  {
	    SLMessage.Error( this, fileName + "  File Does Not Exist !" );
	    return null;
	  }

	  SLTableModel stm = ( SLTableModel )getModel();
	  setCursor( Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ) );
	  setVisible( false );
	  v = stm.readAsCSV( theFile );
	}
	catch( Exception ex )
	{
	  SLMessage.Error( this, "Failed to Import File...   " + fileName );
	  ex.printStackTrace();
	}

	setCursor( Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR ) );
	setVisible( true );
      }
    }

    return v;
  }

  public void redoHeaders()
  {
    getTable().createHeadRenderers();
    initColStuff();
    getTable().updateSortIcons();
  }

  /**
   *  Sets the editability of a column identified by its column ID
   *
   *  @param colId the internal column identifier
   *  @param val true makes column editable, false makes column not editable
   */
  public void setColumnEditable( int colId, boolean val )
  {
    getSLTableModel().setColumnEditable( colId, val );
  }

  /**
   *  Sets the editability of a column specified by its absolute position
   *
   *  @param colNum the column position
   *  @param val true makes column editable, false makes column not editable
   */
  public void setColumnEditableByColNum( int colNum, boolean val )
  {
    getSLTableModel().setColumnEditableByColNum( colNum, val );
  }

  /**
   *  Returns the table model as a SLTableModel
   *
   */
  public SLTableModel getSLTableModel()
  {
    SLTableModel tm = ( SLTableModel )getModel();
    return tm;
  }

  /**
   *	return vector of selected( highlighted ) objects
   */
  public Vector getSelectedRows()
  {
    Vector ret = new Vector();
    int[] selectedRows = getTable().getSelectedRows();
    SLTableModel tm = getSLTableModel();

    for( int i = 0; i < selectedRows.length; ++i )
      ret.addElement( tm.itemAtTableRow( selectedRows[ i ] ) );

    return ret;
  }

  /**
   *  Returns the table as a SLTable
   */
  public SLTable getSLTable()
  {
    SLTable ret = ( SLTable )getTable();
    return ret;
  }

  /**
   *	set column header
   *
   *	@param colId 	SL column id
   *	@param hdr	header string
   */
  public void setColumnHeader( int colId, String hdr )
  {
    int col = ((SLTableModel)getModel()).getColumnIndex( colId );

    if( col < 0 )
      return;

    getColumn( col ).setHeaderValue( hdr );
  }

}
